【小ネタ】Terraform標準機能を組み合わせて、applyコマンドを使用せずにtfstate・tfファイルに既存リソースの定義をインポートしてみた
※本記事ではTerraform v1.7.0を使用しています。
importコマンドとimportブロックについて
importコマンド
importコマンドは、指定した既存リソース及びその設定情報の定義をtfstateファイルに生成するコマンドです。
以下の様に使用します。
terraform import [resourceブロックのtype].[resourceブロックのname] [リソースID]
importブロック
importブロックはTerraform v1.5.0でリリースされた機能です。
importコマンドではリソースの定義はtfstateファイル上にしか生成されず、tfファイルは手書きで編集する必要がありますが、importブロックを使用するとtfファイル上にもリソース定義を自動生成することが可能です。
以下のように定義します。
import { id = [リソースID] to = [resourceブロックのtype].[resourceブロックのname] }
上記を定義した後、「terraform apply -generate-config-out=[任意の名前].tf」コマンドを実行することで「[任意の名前].tf」ファイルが生成され、その中にresourceブロックが生成されます。
また、事前にresourceブロックを定義しておくとTerraformはresourceブロックの自動生成を行わず、既存リソースと事前に定義したresourceブロックの紐づけを行います。
importコマンドとimportブロックの使用方法については過去のブログに記載しているので、より詳細な内容を知りたい方はそちらを読んでみてください。
importブロック使用時のリスク
本記事の概要でも触れていますが、importブロックは非常に便利ですが、terraform applyコマンド実行時に既存リソースに影響を与えてしまうリスクがあります。
例えばインポート先のresource定義を事前に作成しておくパターンでは、以下のようなケースが考えられます。
- importブロック、resourceブロックを定義
- terraform planを実行し、resourceブロックを修正するも設定するAMIのIDを間違える
- terraform applyを実行
- AMIが異なるため、削除 → 再作成という挙動となる
apply実行前に出力される実行内容を確認すれば防ぐことは可能ですが、個人的には万が一があってはならない本番環境等ではterraform applyコマンドは避けたいところです。
terraform applyを使用せずに、tfstate・tfファイルに既存リソースをインポートする方法
ここから、terraform applyを使用せずに、tfstateファイルとtfファイル両方に既存リソースの定義をインポートする方法を紹介します。
① インポート対象リソースの作成
「manabe-test-instance」というNameタグを付けたEC2インスタンスを作成します。
後の手順で使用する為、リソースID、AMI、インスタンスタイプを控えておきます。
② importブロックによるtfファイルの生成
importブロックを定義します。
import { id = "i-0b83e2e9e7d9d13bf" to = aws_instance.sample1 }
「terraform plan -generate-config-out=[任意の名前].tf」コマンドを実行します。
$ terraform plan -generate-config-out=import.tf aws_instance.sample1: Preparing import... [id=i-0b83e2e9e7d9d13bf] aws_instance.sample1: Refreshing state... [id=i-0b83e2e9e7d9d13bf] Planning failed. Terraform encountered an error while generating this plan. ╷ │ Warning: Config generation is experimental │ │ Generating configuration during import is currently experimental, and the generated configuration format may change in future versions. ╵ ╷ │ Error: Conflicting configuration arguments │ │ with aws_instance.sample1, │ on import.tf line 14: │ (source code not available) │ │ "ipv6_address_count": conflicts with ipv6_addresses ╵ ╷ │ Error: Conflicting configuration arguments │ │ with aws_instance.sample1, │ on import.tf line 15: │ (source code not available) │ │ "ipv6_addresses": conflicts with ipv6_address_count
import.tfファイルが生成され、中に「manabe-test-instance」のリソース定義が出力されていますが、共存できないパラメータが生成されてしまっているためplanコマンドの実行結果としてエラーが出力されています。
今回は、「ipv6_addresses」と「ipv6_address_count」が競合していたため、「ipv6_address_count」をコメントアウトし、オプションを付けずにterraform planコマンドを実行します。
$ terraform plan aws_instance.sample1: Preparing import... [id=i-0b83e2e9e7d9d13bf] aws_instance.sample1: Refreshing state... [id=i-0b83e2e9e7d9d13bf] ~~ 省略 ~~ Plan: 1 to import, 0 to add, 0 to change, 0 to destroy.
エラーが消えました。
この時点で、importブロックを削除しておきます。
③ importコマンドによるtfstateファイルへの取り込み
以下のようにimportコマンドを実行します。
$ terraform import aws_instance.sample1 i-0b83e2e9e7d9d13bf aws_instance.sample1: Importing from ID "i-0b83e2e9e7d9d13bf"... aws_instance.sample1: Import prepared! Prepared aws_instance for import aws_instance.sample1: Refreshing state... [id=i-0b83e2e9e7d9d13bf] Import successful! The resources that were imported are shown above. These resources are now in your Terraform state and will henceforth be managed by Terraform.
tfstateファイルの中身を確認します。
無事にリソース定義が生成されていました。
最後に
importブロックを使用することで複数リソースを一度に取り込むことができますが、本手順ではimportコマンドも使用しているうえimportコマンドは1リソースずつしか取り込めない為手順が複雑化してしまっています。
本来は十分に気を付けたうえでimportブロックのみで既存リソースを取り込むことが望ましいと思いますが、どうしても不安がありリスクをなくしたい場合はこの手順を使ってみてください。